import sys
import logging
from typing import TypeVar, Optional
from ..base.fields import FieldInfo
from ..base.plugins import PluginInfo, PluginObject

logger = logging.getLogger(__name__)

T = TypeVar("T", bound="PluginObject")

class PluginHelper:
    def __init__(self, plugin_info: PluginInfo, *argc, **argv):
        self.plugin_name = plugin_info.plugin_name
        self.info = plugin_info

        if argc:
            self.plugin_argc = argc
        else:
            self.plugin_argc = ()

        if argv:
            self.plugin_argv = argv
        else:
            self.plugin_argv = {}

        self.plugin_object: Optional[PluginObject] = None
        self.model = None

    def dispose(self):
        if hasattr(self, 'model'):
            # Get all attributes of the loaded module
            module_attrs = dir(self.model)
            
            # Remove all references to objects defined in the module
            for attr in module_attrs:
                if not attr.startswith('__'):
                    delattr(self.model, attr)
            
            # Remove the module from sys.modules if it's there
            if self.model.__name__ in sys.modules:
                del sys.modules[self.model.__name__]
            
            # Delete the reference to the module in the operator
            del self.plugin_object
            del self.model
        
        # Force garbage collection to clean up any remaining references
        import gc
        gc.collect()
        
        logger.info(f"Disposed plugin: {self.info}")

    @classmethod
    def _create_model(cls, module_name):
        import importlib
        module = importlib.import_module(module_name)
        return module

    @classmethod
    def _create_instance(cls, module, class_name, *args, **kwargs):
        class_ = getattr(module, class_name)
        
        # Get the class's __init__ method argument names
        import inspect
        init_signature = inspect.signature(class_.__init__)
        init_params = init_signature.parameters
        
        # Check if all kwargs are valid for the class's __init__ method
        for kwarg in kwargs:
            if kwarg not in init_params:
                raise ValueError(f"Keyword argument '{kwarg}' is not valid for class '{class_name}', available fields: {init_params.keys()}")
        
        instance = class_(*args, **kwargs)
        return instance

    def create_plugin(self, *args, **kwargs) -> PluginObject:
        if not self.model:
            self.model = PluginHelper._create_model(self.info.plugin_module_name)
        return PluginHelper._create_instance(self.model, self.info.plugin_class_name, *args, **kwargs)
    

    @classmethod
    def check_plugin_module_and_class(cls, plugin_module_name: str, plugin_class_name: str):
        """
        Validate if the given module and class name correspond to a valid PluginObject.

        Parameters:
        plugin_module_name: str
            The name of the module containing the plugin class.
        plugin_class_name: str
            The name of the plugin class.

        Raises:
        ImportError: If the module cannot be imported.
        AttributeError: If the class cannot be found in the module.
        TypeError: If the class is not a subclass of PluginObject.
        """
        import importlib
        module = importlib.import_module(plugin_module_name)
        class_ = getattr(module, plugin_class_name)
        if not issubclass(class_, PluginObject):
            raise TypeError(f"{plugin_class_name} is not a subclass of PluginObject")

    def get_plugin_ojbect(self, **kwargs) -> PluginObject:
        if not self.plugin_object:
            self.plugin_object = self._get_plugin_ojbect(**kwargs)
        return self.plugin_object

    def _get_plugin_ojbect(self, **kwargs) -> PluginObject:
        # Merge class initialization kwargs with method specific kwargs
        full_kwargs = {**self.plugin_argv, **kwargs}
        logger.info(
            "Creating/getting plugin object '%s' with args: %s",
            self.plugin_name,
            full_kwargs,
        )

        # Create the plugin instance
        plugin_instance = self.create_plugin(*self.plugin_argc, **full_kwargs)

        if not isinstance(plugin_instance, PluginObject):
            logger.error(
                "Plugin instance creation failed; returned object is not a PluginObject"
            )
            raise ValueError("Created plugin instance is not a valid PluginObject")

        return plugin_instance
